﻿using Autofac;
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using SqlSugar;
using Swashbuckle.AspNetCore.Filters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using WucCloud.Application;
using WucCloud.Database;
using WucCloud.Utility;

namespace WucCloud.WebApi
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public IWebHostEnvironment Env { get; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="env"></param>
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
            Env = env;
            GlobalContext.LogWhenStart(env);
            GlobalContext.HostingEnvironment = env;
        }

        /// <summary>
        /// 配置应用所需服务，在该方法中可以添加应用所需要的功能或服务
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            GlobalContext.AppSettingConfig = Configuration.GetSection("AppSettingConfig").Get<AppSettingConfig>();
            GlobalContext.Services = services;
            GlobalContext.Configuration = Configuration;

            //In-memory缓存
            services.AddDistributedMemoryCache();

            #region 注入Session 
            //调用AddSession方法
            services.AddSession(options =>
            {
                //Session失效时间
                options.IdleTimeout = TimeSpan.FromMinutes(30);
                //允许跨站点 cookie 使用
                options.Cookie.SameSite = SameSiteMode.Unspecified;
            });

            //防止页面切换后Session ID改变，Seesion失效
            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => false;//默认为true，改为false
                options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
            });
            #endregion


            //注入IHttpContextAccessor, HttpContextAccessor
            services.AddHttpContextHelperAccessor();

            //让服务器端不要返回驼峰格式的数据，即取消服务器的默认序列化为驼峰的规则
            services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);

            //配置Swagger
            services.AddSwaggerGen(c =>
            {
                //注册Swagger生成器，定义一个Swagger文档
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "WucCloud.WebApi", Version = "v1" });

                #region Swagger 显示注释
                //获取应用程序所在目录
                string basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
                //获取xml文件的名字
                string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                //获取xml路径
                string xmlPath = Path.Combine(basePath, xmlFile);
                //显示注释(true表示显示控制器层注释)
                c.IncludeXmlComments(xmlPath, true);
                #endregion

                #region Swagger使用鉴权组件
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Name = "Authorization",
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer",
                    BearerFormat = "JWT",
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)                    
                    Description = "Token（Bearer）令牌"//直接在下框中输入Bearer {token} {注意两者之间有个空格}
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer",
                            }
                        },
                        new string[]{ }
                    }
                });
                #endregion
            });
            
            #region JWT鉴权
            services.AddJWTPower();
            #endregion

            #region 配置角色授权
            //【角色授权】 这么写的好处就是不用在controller中，写多个 roles 。
            // 然后这么写 [Authorize(Policy = "Admin")]
            // 这是写死的角色，正常是需要从数据库读取的
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());//单独角色
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                options.AddPolicy("System", policy => policy.RequireRole("System").Build());
                options.AddPolicy("Others", policy => policy.RequireRole("Others").Build());

                options.AddPolicy("AdminOrSystem", policy => policy.RequireRole("Admin", "System"));//多个角色 ==>或的关系
                options.AddPolicy("A_S_O", policy => policy.RequireRole("Admin", "System", "Others"));
                //options.AddPolicy("SystemAndAdminAndOthers", policy => policy.RequireRole("Admin").RequireRole("System").RequireRole("Others"));//且的关系                
            });
            #endregion

            #region AutoMapper 配置
            //var mapperConfig = new MapperConfiguration(mc =>
            //{
            //    mc.AddProfile(new AutoMapperProfile());
            //});
            //IMapper mapper = mapperConfig.CreateMapper();
            //services.AddSingleton(mapper);

            services.AddAutoMapper(
                    mc=>mc.AddProfile(new AutoMapperProfile()
                ));
            #endregion

            #region Cors跨域配置
            //添加cors 服务 配置跨域处理            
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy", builder =>
                {
                    builder
                    .AllowAnyMethod() //允许任何方式 
                    .AllowAnyHeader()//允许任何头
                    .AllowCredentials()//指定处理cookie  "http://*.*.*.*",
                    .WithOrigins(new[] { "http://192.168.0.104/:8090/" });//代表着允许访问的域，相当于给这个域开放了一个权限，允许访问的权限，可以写多个以逗号分隔                                     
                });
            });
            #endregion

            #region 配置数据库连接            
            var IsMutiDb = Configuration.GetSection("AppSettingConfig:MutiDbEnabled").Value;

            ICacheService sugarCache = new SqlSugarCache();
            //判断是否为多库模式
            if (Convert.ToBoolean(IsMutiDb) == false)
            {
                // =====配置单库=====
                services.AddSqlSugar(new ConnectionConfig
                {
                    ConfigId = GlobalContext.AppSettingConfig.DbNumber, //数据库唯一标识
                    ConnectionString = GlobalContext.AppSettingConfig.DbConnectionString,//数据库连接串
                    DbType = GlobalContext.AppSettingConfig.DbType,//数据库类型
                    IsAutoCloseConnection = true,//是都自动关闭数据库连接
                    InitKeyType = InitKeyType.Attribute,//从特性读取主键自增信息
                    //配置AOP 打印SQL
                    AopEvents = new AopEvents
                    {
                        OnLogExecuting = (sql, pars) =>
                        {
                            Console.WriteLine(SqlProfiler.ParameterFormat("执行后的SQL语句：" + sql, pars));
                            Console.WriteLine();
                        }
                    },
                    ConfigureExternalServices = new ConfigureExternalServices()
                    {
                        //配置缓存类
                        DataInfoCacheService = sugarCache
                    }
                });
            }
            else
            {
                // =====配置多库=====
                List<ConnectionConfig> connectConfigList = new List<ConnectionConfig>();

                //循环遍历 数据库配置信息
                foreach (var item in GlobalContext.AppSettingConfig.DbConfigs)
                {
                    connectConfigList.Add(new ConnectionConfig
                    {
                        ConfigId = item.DbNumber,//数据库序号
                        ConnectionString = item.DbConnectionString,//数据库连接串
                        DbType = item.DbType,//数据库类型
                        IsAutoCloseConnection = true,//是否自动关闭数据库连接
                        InitKeyType = InitKeyType.Attribute,//从特性读取主键自增信息
                        //配置AOP 打印SQL
                        AopEvents = new AopEvents
                        {
                            //多库状态下每个库必须单独绑定打印事件，否则只会打印第一个库的sql日志
                            OnLogExecuting = (sql, pars) =>
                            {
                                Console.WriteLine(SqlProfiler.ParameterFormat("执行后的SQL语句：" + sql, pars));
                                Console.WriteLine();
                            }
                        },
                        ConfigureExternalServices = new ConfigureExternalServices()
                        {
                            //配置缓存类
                            DataInfoCacheService = sugarCache
                        }
                    });
                    
                    services.AddSqlSugar(connectConfigList.ToArray());
                }
            }
            #endregion
        }

        /// <summary>
        /// 配置应用请求处理管道
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();

                //启用中间件服务生成Swagger
                app.UseSwagger();

                //启用中间件服务生成Swagger，指定Swagger JSON终结点
                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "接口文档-基础");
                    //设置根节点访问
                    c.RoutePrefix = string.Empty;
                });
            }

            //路径
            app.UseRouting();

            //使用Session
            app.UseSession();
            //使用当前HttpContext
            app.UseHttpContextHelper();

            //鉴权
            app.UseAuthentication();

            //授权
            app.UseAuthorization();

            //使用自定义跨域中间件
            app.UseMiddleware<CorsMiddleware>();
            //使用跨域
            app.UseCors("CorsPolicy");

            //路由
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        /// <summary>
        /// 配置Autofoc依赖注入
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            var assemblys = Assembly.Load("WucCloud.Application");//Service是继承接口的实现方法类库名称
            var baseType = typeof(IDependency);//IDependency 是一个接口（所有要实现依赖注入的借口都要继承该接口）
            builder.RegisterAssemblyTypes(assemblys).Where(m => baseType.IsAssignableFrom(m) && m != baseType)
              .InstancePerLifetimeScope()//生命周期
              .PropertiesAutowired();//属性注入
            //ControllerBase中使用属性注入
            var controllerBaseType = typeof(ControllerBase);
            builder.RegisterAssemblyTypes(typeof(Program).Assembly)
            .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
            .PropertiesAutowired();

            //批量注册Service
            //Assembly service = Assembly.Load("WucCloud.Application");
            //builder.RegisterAssemblyTypes(service)
            //.Where(t => t.FullName.EndsWith("Service") && !t.IsAbstract) //类名以service结尾，且类型不能是抽象的　
            //.InstancePerLifetimeScope() //生命周期，
            //.PropertiesAutowired(); //属性注入            
        }
    }
}
